#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/delay.h>


int global_count = 0;

#define ERR(msg) do {	\
	printk("err: %s\n", msg);	\
	printk("err_at: %s:%s %d\n", __FILE__, __func__, __LINE__);	\
} while(0)

ssize_t demo_read (struct file *file, char __user *ubuf, size_t size, loff_t *offset)
{
	// local_irq_disable 只是通过屏蔽外部中断的方式， 防止多个外部中断
	// 并发导致的竞态问题。 对于系统调用所产生的竞态是无效的。
	local_irq_disable();
	printk("%s invoked\n", __func__);
	
	printk("global_count = %d\n", global_count);
	copy_to_user(ubuf, &global_count, sizeof(global_count));

	ssleep(1);
	
	global_count += 1;

	local_irq_enable();

	return sizeof(global_count);
}

ssize_t demo_write (struct file *file, const char __user *ubuf, size_t size, loff_t *offset)
{
	printk("%s invoked\n", __func__);

	return size;
}

int demo_open (struct inode *inode, struct file *file)
{
	printk("%s invoked\n", __func__);

	return 0;
}

int demo_close (struct inode *inode, struct file *file)
{
	printk("%s invoked\n", __func__);

	return 0;
}


struct cdev *cdev_p;
int major = 666;
int minor = 0;
unsigned int dev_count = 1;
const char *device_name = "demo_dev";
struct class *cls_p = NULL;
struct device *dev_p = NULL;
struct file_operations fops = {
	.open 		= demo_open,
	.release 	= demo_close,
	.read 		= demo_read,
	.write 		= demo_write,
};

static int __init demo_init(void)
{
	int ret = 0;
	dev_t devt = MKDEV(major, minor);

	printk("%s invoked\n", __func__);

	// ----- 分步注册字符设备驱动 -----
	// 1. 申请字符设备结构体
	cdev_p = cdev_alloc();
	if (NULL == cdev_p) {
		ERR("cdev_alloc");
		goto ERR1;
	}

	// 2. 初始化字符设备结构体
	cdev_init(cdev_p, &fops);

	// 3. 申请设备号
	ret = register_chrdev_region(devt, dev_count, device_name);
	if (ret < 0) {
		printk("errno: %d\n", ret);
		ERR("register_chrdev_region");
		goto ERR2;
	}

	// 4. 注册字符设备驱动
	ret = cdev_add(cdev_p, devt, dev_count);
	if (ret < 0) {
		ERR("cdev_add");
		goto ERR3;
	}

	// ----- 自动创建驱动设备节点 -----

	// 1. 创建设备目录
	cls_p = class_create(THIS_MODULE, device_name);
	if (IS_ERR(cls_p)) {
		ERR("class_create");
		goto ERR4;
	}
	
	// 2. 创建设备文件节点
	dev_p =  device_create(cls_p, NULL, devt, NULL, device_name);
	if (IS_ERR(dev_p)) {
		ERR("device_create");
		goto ERR5;
	}

	return 0;

ERR5:
	class_destroy(cls_p);
	
ERR4:
	cdev_del(cdev_p);
	
ERR3:
	unregister_chrdev_region(devt, dev_count);
	
ERR2:
	kfree(cdev_p);

ERR1:
	return ret;
}

static void __exit demo_exit(void)
{
	printk("%s invoked.\n", __func__);

	device_destroy(cls_p, MKDEV(major, minor));
	class_destroy(cls_p);
	cdev_del(cdev_p);
	unregister_chrdev_region(MKDEV(major, minor), dev_count);
	kfree(cdev_p);

	printk("%s end.\n", __func__);
	
	return ;
}

module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");


